﻿// @brief
// @file CppHeader.cs
// @date 2024年08月17日
// @author doublecat
// 
// Copyright (C) &lt;2024&gt;  &lt;doublecat&gt;
// 
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// 
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
// 
// You should have received a copy of the GNU General Public License
// along with this program.  If not, see <https://www.gnu.org/licenses/>.

using System.CodeDom.Compiler;

namespace doublecat.mcu.svd.output;

/// <summary>
/// 输出cpp头文件
/// </summary>
/// todo: 先输出字符串，在考虑输出到文件
public class CppHeader {
    private SVDDocument _doc;

    /// <summary>
    /// 构造函数
    /// </summary>
    /// <param name="doc">要输出的文档</param>
    public CppHeader(SVDDocument doc) {
        _doc      = doc;
        TabString = "  ";
    }

    /// <summary>
    /// 内缩字符
    /// </summary>
    public string TabString { get; set; }

    /// <summary>
    /// 类型后缀，默认为“_t”
    /// </summary>
    public string TypeSuffix => "_t";

    /// <summary>
    /// 是否组合生成枚举值名称，如果为true，则枚举值名称为“枚举类型_枚举值”，否则为“枚举值”
    /// </summary>
    public bool CompositeEnumValueName { get; set; }

    /// <summary>
    /// 是否组合中断名称（添加设备名前缀）
    /// </summary>
    public bool CompositeInterruptName { get; set; }

    /// <summary>
    /// 输出cpp头文件到指定的路径
    /// </summary>
    /// <param name="path"></param>
    public void Write(string path) {
        // 文件流
        using StreamWriter sw  = new StreamWriter(path);
        IndentedTextWriter itw = new IndentedTextWriter(sw, "  ");
        // 输出device
        Device dev = _doc.GetDevice();
        itw.Indent = 0;
        itw.WriteLine($"// description: {dev.Description}");
        itw.WriteLine($"struct {dev.Name}{TypeSuffix} {{");
        // 输出外设
        itw.Indent++;
        foreach (Peripheral peripheral in dev.Peripherals) {
            string output = Output(peripheral, itw.Indent);
            itw.WriteLine(output);
        }
        itw.Indent--;
        itw.WriteLine($"}}; // end of {dev.Name}");
        // todo 输出枚举;

        // 输出设备地址
        itw.WriteLine("/** 下面的代码应拷贝到链接脚本中");
        itw.WriteLine(OutputAddress(itw.Indent));
        itw.WriteLine("*/");
        // 输出中断
        CompositeInterruptName = true;
        itw.WriteLine("// 中断");
        itw.WriteLine("// ------------------------------------------------------------------------------------");
        itw.WriteLine(OutputInterrupt(itw.Indent));
    }

    /// <summary>
    /// 输出SVDDocument到指定的cpp头文件
    /// </summary>
    /// <param name="doc">svd文档</param>
    /// <param name="path">文件路径</param>
    public void Write(SVDDocument doc, string path) {
        _doc = doc;
        Write(path);
    }

    /// <summary>
    /// 输出位域
    /// </summary>
    /// <param name="field">要输出的位域</param>
    /// <param name="indent">内缩级别</param>
    /// <returns></returns>
    public string Output(Field field, int indent = 0) {
        StringWriter       sw  = new StringWriter();
        IndentedTextWriter itw = new IndentedTextWriter(sw, "  ");
        itw.Indent = indent;
        itw.WriteLine($"// description: {field.Description?.RemoveNewLineChars()}");
        itw.WriteLine($"// offset: {field.BitOffset}");
        itw.WriteLine($"unsigned {field.Name}:{field.BitWidth};");
        return sw.GetStringBuilder().ToString();
    }

    /// <summary>
    /// 输出指定位域枚举
    /// </summary>
    /// <param name="field"></param>
    /// <param name="indent"></param>
    /// <returns></returns>
    /// TODO 枚举名称可能为数字开头，需要特殊处理
    public string OutputEnum(Field field, int indent = 0) {
        if (field.Enumerated?.Count > 0) {
            StringWriter       sw  = new StringWriter();
            IndentedTextWriter itw = new IndentedTextWriter(sw, "  ");
            itw.WriteLine($"enum {field.Enumerated.GetName()}{TypeSuffix} {{");
            itw.Indent++;
            foreach (EnumeratedValue enumeratedValue in field.Enumerated.EnumeratedValues) {
                if (enumeratedValue.Value is not null) {
                    itw.WriteLine(
                        $"{enumeratedValue.GetName(CompositeEnumValueName)} = {enumeratedValue.Value}, // {enumeratedValue.Description?.RemoveNewLineChars()}");
                } else {
                    itw.WriteLine(
                        $"{enumeratedValue.GetName(CompositeEnumValueName)}, // {enumeratedValue.Description?.ReplaceLineEndings()}");
                }
            }
            itw.Indent--;
            itw.WriteLine($"}}; // end of {field.Enumerated.GetName()}");
            return sw.GetStringBuilder().ToString();
        }
        return "";
    }

    /// <summary>
    /// 输出寄存器
    /// </summary>
    /// <param name="register"></param>
    /// <param name="indent"></param>
    /// <returns></returns>
    public string Output(Register register, int indent = 0) {
        StringWriter       sw  = new StringWriter();
        IndentedTextWriter itw = new IndentedTextWriter(sw, "  ");
        itw.Indent = indent;
        itw.WriteLine($"// description: {register.Description?.RemoveNewLineChars()}");
        itw.WriteLine($"// offset: {register.AddressOffset}");
        // check field's count
        if (register.Fields.Count > 0) {
            // fields
            itw.WriteLine($"struct {register.GetTypeName()}{TypeSuffix} {{");
            itw.Indent++;
            foreach (Field field in register.Fields) {
                string fops = Output(field, itw.Indent);
                itw.WriteLine(fops);
            }
            itw.Indent--;
            itw.WriteLine($"}} {register.GetVarName()}; // end of {register.Name}");
        } else {
            uint size = register.GetSize();
            itw.WriteLine($"{size.ToDataType()} {register.GetVarName()}; // no fields");
        }
        return sw.GetStringBuilder().ToString();
    }

    /// <summary>
    /// 输出peripheral部分
    /// </summary>
    /// <param name="peripheral"></param>
    /// <param name="indent"></param>
    /// <returns></returns>
    public string Output(Peripheral peripheral, int indent = 0) {
        StringWriter       sw  = new StringWriter();
        IndentedTextWriter itw = new IndentedTextWriter(sw, "  ");
        itw.Indent = indent;
        itw.WriteLine($"// description: {peripheral.Description?.RemoveNewLineChars()}");
        itw.WriteLine($"struct {peripheral.Name}{TypeSuffix} {{");
        itw.Indent++;
        // 检查peripheral是否从他设备继承？
        List<Register> registers = string.IsNullOrEmpty(peripheral.DerivedFrom)
            ? peripheral.Registers
            : peripheral.Parent.FindPeripheral(peripheral.DerivedFrom)?.Registers ??
              throw new InvalidOperationException($"not found derived from peripheral:{peripheral.DerivedFrom}");
        foreach (Register register in registers) {
            string rs = Output(register, itw.Indent);
            itw.WriteLine(rs);
        }
        itw.Indent--;
        itw.WriteLine($"}} {peripheral.Name}; // end of {peripheral.Name}");

        return sw.GetStringBuilder().ToString();
    }

    /// <summary>
    /// 输出全部外设外设的地址
    /// </summary>
    /// <param name="indent"></param>
    /// <returns></returns>
    public string OutputAddress(int indent = 0) {
        StringWriter       sw  = new StringWriter();
        IndentedTextWriter itw = new IndentedTextWriter(sw, "  ");

        List<Peripheral> peripherals = _doc.GetDevice().Peripherals;
        itw.Indent = indent;
        foreach (Peripheral peripheral in peripherals) {
            itw.WriteLine($"PROVIDE({peripheral.GetName()} = {peripheral.BaseAddress:X8});");
        }

        return sw.GetStringBuilder().ToString();
    }

    /// <summary>
    /// 输出全部的设备中断
    /// </summary>
    /// <param name="indent"></param>
    /// <returns></returns>
    public string OutputInterrupt(int indent = 0) {
        StringWriter       sw  = new StringWriter();
        IndentedTextWriter itw = new IndentedTextWriter(sw, "  ");

        foreach (Peripheral peripheral in _doc.GetDevice().Peripherals) {
            foreach (Peripheral.Interrupt interrupt in peripheral.Interrupts) {
                itw.Indent = indent;
                itw.WriteLine($"// description: {interrupt.Description?.RemoveNewLineChars()}");
                itw.WriteLine($"// value: {interrupt.Value}");
                if (CompositeInterruptName) {
                    itw.WriteLine(
                        $"void {peripheral.GetName()}_{interrupt.Name}() __attribute__((used,weak,interrupt));");
                } else {
                    itw.WriteLine($"void {interrupt.Name}() __attribute__((used,weak,interrupt));");
                }
            }
        }

        return sw.GetStringBuilder().ToString();
    }
}

/// <summary>
/// 
/// </summary>
public static class SVDOutputHeader {
    /// <summary>
    /// 移除字符串中的换行符
    /// </summary>
    /// <param name="str"></param>
    /// <returns></returns>
    public static string RemoveNewLineChars(this string str) {
        return str.Replace("\r", "").Replace("\n", "");
    }

    /// <summary>
    /// 讲一个整数字符串转换成对应大小的整数类型，例如：“32” -> uint32_t
    /// </summary>
    /// <param name="str"></param>
    /// <returns></returns>
    public static string ToDataType(this uint size) {
        return size switch {
            8  => "uint8_t",
            16 => "uint16_t",
            32 => "uint32_t",
            _  => throw new Exception($"unknown data size: {size}")
        };
    }
}
